Android OpenGLES绘制yuv420纹理

  1. 把shader代码写入raw里面

vertex_shader.glsl

1
2
3
4
5
6
7
attribute vec4 av_Position;//顶点位置
attribute vec2 af_Position;//纹理位置
varying vec2 v_texPo;//纹理位置 与fragment_shader交互
void main() {
v_texPo = af_Position;
gl_Position = av_Position;
}

fragment_shader.glsl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
precision mediump float;//精度 为float
varying vec2 v_texPo;//纹理位置 接收于vertex_shader
uniform sampler2D sampler_y;//纹理y
uniform sampler2D sampler_u;//纹理u
uniform sampler2D sampler_v;//纹理v

void main() {
//yuv420->rgb
float y,u,v;
y = texture2D(sampler_y,v_texPo).r;
u = texture2D(sampler_u,v_texPo).r- 0.5;
v = texture2D(sampler_v,v_texPo).r- 0.5;
vec3 rgb;
rgb.r = y + 1.403 * v;
rgb.g = y - 0.344 * u - 0.714 * v;
rgb.b = y + 1.770 * u;

gl_FragColor=vec4(rgb,1);
}

因为OpenGLES需要用rgb来加载显示,这里就需要将yuvrgb,这里放在OpenGL里面转换,OpenGL里面使用GPU,提高性能。

  1. 数据写入

YUV420Texture.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174


import android.content.Context;
import android.opengl.GLES20;


import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class YUV420Texture {

private Context context;

//顶点坐标
static float vertexData[] = { // in counterclockwise order:
-1f, -1f, 0.0f, // bottom left
1f, -1f, 0.0f, // bottom right
-1f, 1f, 0.0f, // top left
1f, 1f, 0.0f, // top right
};

//纹理坐标
static float textureData[] = { // in counterclockwise order:
0f, 1f, 0.0f, // bottom left
1f, 1f, 0.0f, // bottom right
0f, 0f, 0.0f, // top left
1f, 0f, 0.0f, // top right
};

//每一次取点的时候取几个点
static final int COORDS_PER_VERTEX = 3;

private final int vertexCount = vertexData.length / COORDS_PER_VERTEX;
//每一次取的总的点 大小
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

//位置
private FloatBuffer vertexBuffer;
//纹理
private FloatBuffer textureBuffer;

private int program;

//顶点位置
private int avPosition;
//纹理位置
private int afPosition;

//shader yuv变量
private int sampler_y;
private int sampler_u;
private int sampler_v;
private int[] textureId_yuv;


//YUV数据
private int width_yuv;
private int height_yuv;
private ByteBuffer y;
private ByteBuffer u;
private ByteBuffer v;


public YUV420Texture(Context context) {
this.context = context;

vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertexData);
vertexBuffer.position(0);

textureBuffer = ByteBuffer.allocateDirect(textureData.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(textureData);
textureBuffer.position(0);
}

public void initYUV() {
String vertexSource = ShaderUtil.readRawTxt(context, R.raw.vertex_shader);
String fragmentSource = ShaderUtil.readRawTxt(context, R.raw.fragment_shader);
program = ShaderUtil.createProgram(vertexSource, fragmentSource);
if (program > 0) {
//获取顶点坐标字段
avPosition = GLES20.glGetAttribLocation(program, "av_Position");
//获取纹理坐标字段
afPosition = GLES20.glGetAttribLocation(program, "af_Position");
//获取yuv字段
sampler_y = GLES20.glGetUniformLocation(program, "sampler_y");
sampler_u = GLES20.glGetUniformLocation(program, "sampler_u");
sampler_v = GLES20.glGetUniformLocation(program, "sampler_v");

textureId_yuv = new int[3];
//创建3个纹理
GLES20.glGenTextures(3, textureId_yuv, 0);

//绑定纹理
for (int id : textureId_yuv) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
//环绕(超出纹理坐标范围) (s==x t==y GL_REPEAT 重复)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
//过滤(纹理像素映射到坐标点) (缩小、放大:GL_LINEAR线性)
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
}

}

}

public void setYUVData(int width, int height, byte[] y, byte[] u, byte[] v) {
this.width_yuv = width;
this.height_yuv = height;
this.y = ByteBuffer.wrap(y);
this.u = ByteBuffer.wrap(u);
this.v = ByteBuffer.wrap(v);
}

public void draw() {
if (width_yuv > 0 && height_yuv > 0 && y != null && u != null && v != null) {
GLES20.glUseProgram(program);
GLES20.glEnableVertexAttribArray(avPosition);
GLES20.glVertexAttribPointer(avPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

GLES20.glEnableVertexAttribArray(afPosition);
GLES20.glVertexAttribPointer(afPosition, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureBuffer);

//激活纹理0来绑定y数据
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[0]);
//glTexImage2D (int target,
// int level,
// int internalformat,
// int width,
// int height,
// int border,
// int format,
// int type,
// Buffer pixels)
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv, height_yuv, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, y);

//激活纹理1来绑定u数据
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[1]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv / 2, height_yuv / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, u);

//激活纹理2来绑定u数据
GLES20.glActiveTexture(GLES20.GL_TEXTURE2);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId_yuv[2]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, width_yuv / 2, height_yuv / 2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, v);

//给fragment_shader里面yuv变量设置值 0 1 2 标识纹理x
GLES20.glUniform1i(sampler_y, 0);
GLES20.glUniform1i(sampler_u, 1);
GLES20.glUniform1i(sampler_v, 2);

//绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);

y.clear();
u.clear();
v.clear();
y = null;
u = null;
v = null;
GLES20.glDisableVertexAttribArray(afPosition);
GLES20.glDisableVertexAttribArray(avPosition);

}
}
}

ShaderUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

import android.content.Context;
import android.opengl.GLES20;
import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

public class ShaderUtil {
private static final String TAG = "ShaderUtil";


public static String readRawTxt(Context context, int rawId) {
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}

public static int loadShader(int shaderType, String source) {
// create a vertex shader type (GLES20.GL_VERTEX_SHADER)
// or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
//添加代码到shader
GLES20.glShaderSource(shader, source);
//编译shader
GLES20.glCompileShader(shader);
int[] compile = new int[1];
//检测是否编译成功
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
if (compile[0] != GLES20.GL_TRUE) {
Log.d(TAG, "shader compile error");
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}

public static int createProgram(String vertexSource, String fragmentSource) {
//获取vertex shader
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
//获取fragment shader
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
return 0;
}
//创建一个空的渲染程序
int program = GLES20.glCreateProgram();
if (program != 0) {
//添加vertexShader到渲染程序
GLES20.glAttachShader(program, vertexShader);
//添加fragmentShader到渲染程序
GLES20.glAttachShader(program, fragmentShader);
//关联为可执行渲染程序
GLES20.glLinkProgram(program);
int[] linsStatus = new int[1];
//检测是否关联成功
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linsStatus, 0);
if (linsStatus[0] != GLES20.GL_TRUE) {
Log.d(TAG, "link program error");
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;

}

}

  1. Render书写
    MyRender.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class MyRender implements GLSurfaceView.Renderer {

private Context context;

private YUV420Texture yuv420Texture;

public MyRender(Context context) {
this.context = context;
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
yuv420Texture = new YUV420Texture(context);
yuv420Texture.initYUV();
}


@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//宽高
GLES20.glViewport(0, 0, width, height);
}

@Override
public void onDrawFrame(GL10 gl) {
//清空颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
//设置背景颜色
// GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

yuv420Texture.draw();
}

public void setYuvData(int width, int height, byte[] y, byte[] u, byte[] v) {
if (yuv420Texture != null) {
yuv420Texture.setYUVData(width, height, y, u, v);
}
}
}
  1. GLSurfaceView引用Renderer

MyGLSurfaceView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;

public class MyGLSurfaceView extends GLSurfaceView {

private MyRender myRender;

public MyGLSurfaceView(Context context) {
this(context, null);
}

public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(2);
myRender = new MyRender(context);
setRenderer(myRender);
//mode=GLSurfaceView.RENDERMODE_WHEN_DIRTY之后 调用requestRender()触发Render的onDrawFrame函数
//mode=GLSurfaceView.RENDERMODE_CONTINUOUSLY之后 自动调用onDrawFrame 60fps左右
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}

public void setYUVData(int width, int height, byte[] y, byte[] u, byte[] v) {
if (myRender != null) {
myRender.setYuvData(width, height, y, u, v);
requestRender();
}
}
}

-------------The End-------------